package com.atguigu.gulimall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.cart.feign.ProductFeignService;
import com.atguigu.gulimall.cart.interceptor.CartInterceptor;
import com.atguigu.gulimall.cart.service.CartService;
import com.atguigu.gulimall.cart.to.SkuInfoTo;
import com.atguigu.gulimall.cart.to.UserInfoTo;
import com.atguigu.gulimall.cart.vo.CartItemVo;
import com.atguigu.gulimall.cart.vo.CartVo;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @DESCRIPTION: 购物车实现类
 * @author: zr
 * @DATE: 2023/1/30 22:42
 */
@Service
public class CartServiceImpl implements CartService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private ProductFeignService productFeignService;
    @Autowired
    private ThreadPoolExecutor executor;
    private final String CART_PREFIX = "gulimall:cart";

    @Override
    public CartItemVo addCartItem(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        String result = (String) cartOps.get(skuId.toString());
        if (StringUtils.isEmpty(result)) {
            // 添加新商品到购物车
            // 调用远程服务根据skuId查询商品信息
            CartItemVo cartItemVo = new CartItemVo();
            // 使用异步多线程 减少接口时间
            CompletableFuture<Void> skuInfoFuture = CompletableFuture.runAsync(() -> {
                R info = productFeignService.info(skuId);
                if (info.getCode() == 0) {
                    // 响应成功 得到商品信息
                    SkuInfoTo skuInfo = info.getData("skuInfo", new TypeReference<SkuInfoTo>() {
                    });
                    cartItemVo.setSkuId(skuId);
                    cartItemVo.setTitle(skuInfo.getSkuTitle());
                    cartItemVo.setImage(skuInfo.getSkuDefaultImg());
                    cartItemVo.setPrice(skuInfo.getPrice());
                    cartItemVo.setCount(num);
                    cartItemVo.setCheck(true);
                }
            }, executor);
            CompletableFuture<Void> skuSaleAttrFuture = CompletableFuture.runAsync(() -> {
                // 调用远程服务根据skuId得到销售属性
                List<String> skuSaleAttrValues = productFeignService.getSkuSaleAttrValues(skuId);
                cartItemVo.setSkuAttr(skuSaleAttrValues);
            }, executor);
            // 等待两个多线程全部执行完成后
            CompletableFuture.allOf(skuInfoFuture, skuSaleAttrFuture).get();
            // 将商品信息存入redis
            String s = JSON.toJSONString(cartItemVo);
            cartOps.put(skuId.toString(), s);
            return cartItemVo;
        } else {
            //将得到的数据转为实体
            CartItemVo cartItemVo = JSON.parseObject(result, CartItemVo.class);
            cartItemVo.setCount(num + cartItemVo.getCount());
            String s = JSON.toJSONString(cartItemVo);
            cartOps.put(skuId.toString(), s);
            return cartItemVo;
        }

    }

    @Override
    public CartItemVo getCartItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        String cartItemStr = (String) cartOps.get(skuId.toString());
        CartItemVo cartItemVo = JSON.parseObject(cartItemStr, CartItemVo.class);
        return cartItemVo;
    }

    @Override
    public CartVo getCart() throws ExecutionException, InterruptedException {
        CartVo cartVo = new CartVo();
        // 从threadLocal 中获取用户信息
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        // 判断是否登录
        Long userId = userInfoTo.getUserId();
        String userKey = userInfoTo.getUserKey();
        if (Objects.nonNull(userId)) {
            // 1、登录
            String cartKey = CART_PREFIX + userId;
            String tempCartKey = CART_PREFIX + userKey;
            // 如果临时购物车的数据还没有进行合并 [合并购物车]
            List<CartItemVo> tempCartItems = getCartItems(tempCartKey);
            if (!CollectionUtils.isEmpty(tempCartItems)) {
                // 临时数据不为空时 需要添加到购物车
                for (CartItemVo tempCartItem : tempCartItems) {
                    addCartItem(tempCartItem.getSkuId(), tempCartItem.getCount());
                }
                // 清空临时购物车数据
                deleteCart(tempCartKey);
            }
            // 获取登录后的购物车数据
            List<CartItemVo> cartItems = getCartItems(cartKey);
            cartVo.setItems(cartItems);
        } else {
            // 未登录 是临时用户
            String cartKey = CART_PREFIX + userKey;
            List<CartItemVo> cartItemVos = getCartItems(cartKey);
            cartVo.setItems(cartItemVos);
        }
        return cartVo;
    }

    @Override
    public void deleteCart(String cartKey) {
        redisTemplate.delete(cartKey);
    }

    @Override
    public void checkItem(Long skuId, Integer checked) {
        // 获取单个商品项
        CartItemVo cartItem = getCartItem(skuId);
        cartItem.setCheck(checked == 1 ? true : false);
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        // 修改redis种要保存的数据
        String cartStr = JSON.toJSONString(cartItem);
        cartOps.put(skuId.toString(), cartStr);
    }

    @Override
    public void countItem(Long skuId, Integer num) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        CartItemVo cartItem = getCartItem(skuId);
        cartItem.setCount(num);
        String cartStr = JSON.toJSONString(cartItem);
        cartOps.put(skuId.toString(), cartStr);
    }

    @Override
    public void deleteItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        // 在redis种删除指定购物项
        cartOps.delete(skuId.toString());
    }

    @Override
    public List<CartItemVo> getUserCartItems() {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        // 判断是否登录
        if (Objects.isNull(userInfoTo.getUserId())) {
            return Lists.newArrayList();
        } else {
            // 已经登录
            String userKey = CART_PREFIX + userInfoTo.getUserId();
            List<CartItemVo> cartItems = getCartItems(userKey);
            List<CartItemVo> itemVoList = cartItems.stream().filter(CartItemVo::getCheck).map(item -> {
                // 商品的价格需要实时查询
                BigDecimal skuPrice = productFeignService.getPrice(item.getSkuId());
                item.setPrice(skuPrice);
                return item;
            }).collect(Collectors.toList());
            return itemVoList;
        }
    }

    /**
     * 根据cartKeyu获取购物车中所有的购物项数据
     *
     * @param cartKey
     * @return
     */
    private List<CartItemVo> getCartItems(String cartKey) {
        BoundHashOperations<String, Object, Object> cartOps = redisTemplate.boundHashOps(cartKey);
        // 获取购物车数据
        List<Object> cartData = cartOps.values();
        if (!CollectionUtils.isEmpty(cartData)) {
            // 获取购物车中的购物项数据
            List<CartItemVo> cartItemVos = cartData.stream().map(object -> {
                String cartStr = (String) object;
                CartItemVo cartItemVo = JSON.parseObject(cartStr, CartItemVo.class);
                return cartItemVo;
            }).collect(Collectors.toList());
            return cartItemVos;
        }
        return Lists.newArrayList();
    }

    /**
     * 获取到我们要操作的购物车
     *
     * @return
     */
    private BoundHashOperations<String, Object, Object> getCartOps() {
        // 获取用户信息
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        // 判断是使用登录后的购物车还是临时
        String cartKey = "";
        Long userId = userInfoTo.getUserId();
        if (Objects.nonNull(userId)) {
            // 已经登录
            cartKey = CART_PREFIX + userId;
        } else {
            cartKey = CART_PREFIX + userInfoTo.getUserKey();
        }
        BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);
        return operations;
    }
}
